Context
In this exercise we are going to study the results from a particle analysis performed on a set of SEM images of Ni nanoparticles.
To obtain these nanoparticles, we start from Si wafers on which a 5 or 10 nm layer of Ni is deposited by PVD. These wafers are then heated in an H2 atmosphere to reduce them, which provokes the formation of nanoparticles through unwetting of the Si surface. These nanoparticles are then used as catalyst for the growth of vertically aligned carbon nanotubes by PECVD. As the diameter and density of the tubes are directly related to the diameter and density of the nanoparticles, we are interested in getting a clear idea of these parameters before performing the nanotube growth.
Some Ni-covered Si wafers were prepared before the first confinement (substrates labeled as old), some were prepared in September (new substrates). Here, we are interested in seeing whether the age and thickness of the Ni layer plays a role on the nanoparticles size and density. Also, the other parameters to study are the temperature at which the unwetting is performed, as well as the duration of this reaction.
To perform this study, we prepared samples from various substrates at various temperatures and during various times. The substrates are then observed with SEM, and several pictures are taken to increase the statistics. These pictures are then analyzed with ImageJ, as shown on Figure 1.1.
In this exercise, we are going to treat the tables obtained from ImageJ: these tables contain the (x,y) positions of the particles as well as their area.
Data wrangling
- Load the packages
tidyverse, readxl, units, broom, and ggforce. We are going to getting used to work with units in this exercise, which is a very good habit to take in order to avoid many unit conversion problems. The package ggforce allows doing ggplot plots with tibbles containing units. Set the global ggplot2 theme to black and white. Also, make it so that the strip.background (background of the facets titles) is blank, and that the strip.text is bold.
library(tidyverse)
library(readxl)
library(units)
library(ggforce)
library(broom)
theme_set(theme_bw()+
theme(strip.background = element_blank(),
strip.text=element_text(face = "bold", size=14)
))
- Find all MLxx_xx_cc.csv files in the
Data folder and store them in flist.
flist <- list.files(path="Data", pattern="m.csv")
- Read the
sample.xlsx file that contain all characteristics of the various samples, such as their temperature, substrate type, time of reaction, and store the result in samples.
- Modify the tibble
samples so that its columns are named “sample”, “T”, “time”, and “substrate”
- Using
separate(), separate the “substrate” column into “sub_thick” and “sub_age” containing the thickness and age of the substrate. Use convert = TRUE to convert the characters to integers if applicable.
- Give the columns their proper unit when applicable (refer to this example)
samples <- read_excel("Data/sample.xlsx") %>%
rename(sample = "name",
T = "Temperature (°C)",
time = "Time (minutes)",
substrate = "Substrate (nm)") %>%
separate(substrate, c("sub_thick","sub_age"), convert = TRUE) %>%
mutate(sub_thick = set_units(sub_thick,"nm"),
time = set_units(time,"min"),
T = set_units(T,"degC")
)
- Create the
readfile(filename) function that, given one of the csv files names, will:
- Determine the unit used in this file: from
filename, store the unit into the variable UNIT (that should be the string "um" or "nm"). You can use unlist(strsplit(filename,"_")) to get a vector of the elements of filename separated by a _ character.
- Read the csv file, then successively:
- Select only the X, Y and Area columns (some files have more columns) and rename them to lowercases names.
- Add the
file column containing the filename, and then separate it into 3 columns sample, number and unit
- Remove the
unit column
- Apply the corresponding units to x, y and area. You can apply a unit to a vector
x based on a string UNIT using set_units(x, UNIT, mode = "standard").
- Create a column
diameter containing the diameter of the particles.
- Test this function on 2 files with 2 different units to check that it gives the expected result.
readfile <- function(filename){
UNIT <- unlist(strsplit(filename,"_"))
UNIT <- gsub(".csv","",UNIT[length(UNIT)])
read_csv(filename) %>%
rename_all(tolower) %>%
select(x,y,area) %>%
mutate(file=gsub("Data/","",filename)) %>%
separate(file, c("sample","number","unit"), sep="_") %>%
select(-unit) %>%
mutate(x = set_units(x, UNIT, mode = "standard"),
y = set_units(y, UNIT, mode = "standard"),
area = set_units(area, paste0(UNIT,"*",UNIT), mode = "standard"),
diameter = sqrt(4*area/pi))
}
readfile("Data/ML16_02_um.csv")
## # A tibble: 100 × 6
## x y area sample number diameter
## [um] [um] [um^2] <chr> <chr> [um]
## 1 0.221 0.116 0.006 ML16 02 0.0874
## 2 0.328 0.273 0.009 ML16 02 0.107
## 3 0.497 0.423 0.007 ML16 02 0.0944
## 4 0.189 0.638 0.011 ML16 02 0.118
## 5 0.421 1.07 0.01 ML16 02 0.113
## 6 0.222 1.41 0.007 ML16 02 0.0944
## 7 0.634 1.44 0.011 ML16 02 0.118
## 8 0.634 1.44 0.011 ML16 02 0.118
## 9 0.408 1.58 0.009 ML16 02 0.107
## 10 1.68 0.294 0.009 ML16 02 0.107
## # … with 90 more rows
readfile("Data/ML16_04_nm.csv")
## # A tibble: 95 × 6
## x y area sample number diameter
## [nm] [nm] [nm^2] <chr> <chr> [nm]
## 1 485. 176. 8178. ML16 04 102.
## 2 553. 422. 9433. ML16 04 110.
## 3 907. 82.5 14150. ML16 04 134.
## 4 781. 194. 5124. ML16 04 80.8
## 5 762. 448. 9433. ML16 04 110.
## 6 521. 928. 16118. ML16 04 143.
## 7 782. 654. 8008. ML16 04 101.
## 8 906. 1082. 9501. ML16 04 110.
## 9 870. 1270. 8381. ML16 04 103.
## 10 163. 1767. 10010. ML16 04 113.
## # … with 85 more rows
- Using
readfile() that you just defined, read all csv files and store them into a tidy tibble called particles. Do not use a for loop to do so. Join this table with the samples one. You will see that, since we attributed units to some columns, all data are automatically converted to a single unit. Filter the data for diameters lower than 40 µm as some very large particles were detected in the image processing that are actually not particles. The resulting tibble should look like this:
particles <- tibble(file=flist) %>%
mutate(data = map(file, ~readfile(file.path("Data",.)))) %>%
unnest(data) %>%
inner_join(samples) %>%
filter(diameter<set_units(40,"um"))
## # A tibble: 6,446 × 11
## file x y area sample number diameter T sub_thick sub_age time
## <chr> [um] [um] [um^2] <chr> <chr> [um] [°C] [nm] <chr> [min]
## 1 ML15_… 0.236 0.043 0.004 ML15 01 0.0714 750 5 old 20
## 2 ML15_… 0.108 0.147 0.004 ML15 01 0.0714 750 5 old 20
## 3 ML15_… 0.216 0.222 0.003 ML15 01 0.0618 750 5 old 20
## 4 ML15_… 0.108 0.266 0.002 ML15 01 0.0505 750 5 old 20
## 5 ML15_… 0.241 0.336 0.002 ML15 01 0.0505 750 5 old 20
## 6 ML15_… 0.232 0.497 0.001 ML15 01 0.0357 750 5 old 20
## 7 ML15_… 0.232 0.496 0.003 ML15 01 0.0618 750 5 old 20
## 8 ML15_… 0.342 0.605 0.005 ML15 01 0.0798 750 5 old 20
## 9 ML15_… 0.112 0.815 0.004 ML15 01 0.0714 750 5 old 20
## 10 ML15_… 0.296 0.77 0.006 ML15 01 0.0874 750 5 old 20
## # … with 6,436 more rows
In case you didn’t manage to get there, here is the particles tibble (it doesn’t contain the units though as you can’t save it in a text file.)
LS0tCnRpdGxlIDogIlIgRXhlcmNpc2VzIC0gUGFydGljbGUgYW5hbHlzaXMgZnJvbSBTRU0gaW1hZ2VzIgpkYXRlICA6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICAgIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgICAgICB0b2MgICAgICAgICAgICA6IFRSVUUKICAgICAgICB0b2NfZmxvYXQgICAgICA6IFRSVUUKICAgICAgICB0b2NfZGVwdGggICAgICA6IDQKICAgICAgICBoaWdobGlnaHQgICAgICA6IHRhbmdvCiAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFCiAgICAgICAgY29kZV9kb3dubG9hZCAgOiBUUlVFCnBhcmFtczogCiAgICBzb2x1dGlvbjoKICAgICAgICB2YWx1ZTogdHJ1ZQotLS0KCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpibG9ja3F1b3RlIHsKICBiYWNrZ3JvdW5kOiAjRTlGOUZGOwogIGJvcmRlci1sZWZ0OiA1cHggc29saWQgIzAyNjA4NjsKICBtYXJnaW46IDEuNWVtIDEwcHg7CiAgcGFkZGluZzogMC41ZW0gMTBweDsKICBmb250LXNpemU6IDFlbTsKfQo8L3N0eWxlPgoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIifQpsaWJyYXJ5KGRvd25sb2FkdGhpcykKZG93bmxvYWRfbGluaygKICBsaW5rID0gIi4vQXJjaGl2ZS56aXAiLAogIG91dHB1dF9uYW1lID0gIkRhdGEgRmlsZXMiLAogIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBEYXRhIEZpbGVzIiwKICBidXR0b25fdHlwZSA9ICJkZWZhdWx0IiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKb3B0aW9ucyh3aWR0aCA9IDgwKQpgYGAKPGJyPgoKLS0tLQoKIyBDb250ZXh0CgpJbiB0aGlzIGV4ZXJjaXNlIHdlIGFyZSBnb2luZyB0byBzdHVkeSB0aGUgcmVzdWx0cyBmcm9tIGEgcGFydGljbGUgYW5hbHlzaXMgcGVyZm9ybWVkIG9uIGEgc2V0IG9mIFNFTSBpbWFnZXMgb2YgTmkgbmFub3BhcnRpY2xlcy4gCgpUbyBvYnRhaW4gdGhlc2UgbmFub3BhcnRpY2xlcywgd2Ugc3RhcnQgZnJvbSBTaSB3YWZlcnMgb24gd2hpY2ggYSA1IG9yIDEwIG5tIGxheWVyIG9mIE5pIGlzIGRlcG9zaXRlZCBieSBQVkQuIFRoZXNlIHdhZmVycyBhcmUgdGhlbiBoZWF0ZWQgaW4gYW4gSH4yfiBhdG1vc3BoZXJlIHRvIHJlZHVjZSB0aGVtLCB3aGljaCBwcm92b2tlcyB0aGUgZm9ybWF0aW9uIG9mIG5hbm9wYXJ0aWNsZXMgdGhyb3VnaCB1bndldHRpbmcgb2YgdGhlIFNpIHN1cmZhY2UuIFRoZXNlIG5hbm9wYXJ0aWNsZXMgYXJlIHRoZW4gdXNlZCBhcyBjYXRhbHlzdCBmb3IgdGhlIGdyb3d0aCBvZiB2ZXJ0aWNhbGx5IGFsaWduZWQgY2FyYm9uIG5hbm90dWJlcyBieSBQRUNWRC4gQXMgdGhlIGRpYW1ldGVyIGFuZCBkZW5zaXR5IG9mIHRoZSB0dWJlcyBhcmUgZGlyZWN0bHkgcmVsYXRlZCB0byB0aGUgZGlhbWV0ZXIgYW5kIGRlbnNpdHkgb2YgdGhlIG5hbm9wYXJ0aWNsZXMsIHdlIGFyZSBpbnRlcmVzdGVkIGluIGdldHRpbmcgYSBjbGVhciBpZGVhIG9mIHRoZXNlIHBhcmFtZXRlcnMgYmVmb3JlIHBlcmZvcm1pbmcgdGhlIG5hbm90dWJlIGdyb3d0aC4KClNvbWUgTmktY292ZXJlZCBTaSB3YWZlcnMgd2VyZSBwcmVwYXJlZCBiZWZvcmUgdGhlIGZpcnN0IGNvbmZpbmVtZW50IChzdWJzdHJhdGVzIGxhYmVsZWQgYXMgYG9sZGApLCBzb21lIHdlcmUgcHJlcGFyZWQgaW4gU2VwdGVtYmVyIChgbmV3YCBzdWJzdHJhdGVzKS4gSGVyZSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gc2VlaW5nIHdoZXRoZXIgdGhlIGFnZSBhbmQgdGhpY2tuZXNzIG9mIHRoZSBOaSBsYXllciBwbGF5cyBhIHJvbGUgb24gdGhlIG5hbm9wYXJ0aWNsZXMgc2l6ZSBhbmQgZGVuc2l0eS4gQWxzbywgdGhlIG90aGVyIHBhcmFtZXRlcnMgdG8gc3R1ZHkgYXJlIHRoZSB0ZW1wZXJhdHVyZSBhdCB3aGljaCB0aGUgdW53ZXR0aW5nIGlzIHBlcmZvcm1lZCwgYXMgd2VsbCBhcyB0aGUgZHVyYXRpb24gb2YgdGhpcyByZWFjdGlvbi4KClRvIHBlcmZvcm0gdGhpcyBzdHVkeSwgd2UgcHJlcGFyZWQgc2FtcGxlcyBmcm9tIHZhcmlvdXMgc3Vic3RyYXRlcyBhdCB2YXJpb3VzIHRlbXBlcmF0dXJlcyBhbmQgZHVyaW5nIHZhcmlvdXMgdGltZXMuIFRoZSBzdWJzdHJhdGVzIGFyZSB0aGVuIG9ic2VydmVkIHdpdGggU0VNLCBhbmQgc2V2ZXJhbCBwaWN0dXJlcyBhcmUgdGFrZW4gdG8gaW5jcmVhc2UgdGhlIHN0YXRpc3RpY3MuIFRoZXNlIHBpY3R1cmVzIGFyZSB0aGVuIGFuYWx5emVkIHdpdGggW0ltYWdlSl0oaHR0cHM6Ly9pbWFnZWoubmV0L1dlbGNvbWUpLCBhcyBzaG93biBvbiBGaWd1cmUgXEByZWYoZmlnOlNFTWltYWdlcykuIAoKYGBge3IgU0VNaW1hZ2VzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBmaWcuY2FwPSJUeXBpY2FsIFNFTSBpbWFnZSBvZiBOaSBuYW5vcGFydGljbGVzOiBmcm9tIHRoZSByYXcgaW1hZ2UgdG8gcGFydGljbGUgYW5hbHlzaXMiLCBmaWcuYWxpZ249ImNlbnRlciIsIG91dC53aWR0aD0iMzMlIiwgZmlnLnNob3c9J2hvbGQnfQpteWltYWdlcyA8LSBjKCJEYXRhL01MMjZfMDEucG5nIiwgIkRhdGEvTUwyNl8wMS10aHJlc2hvbGQucG5nIiwgIkRhdGEvTUwyNl8wMS1wYXJ0aWNsZXMucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MobXlpbWFnZXMpCmBgYAoKSW4gdGhpcyBleGVyY2lzZSwgd2UgYXJlIGdvaW5nIHRvIHRyZWF0IHRoZSB0YWJsZXMgb2J0YWluZWQgZnJvbSBJbWFnZUo6IHRoZXNlIHRhYmxlcyBjb250YWluIHRoZSAoeCx5KSBwb3NpdGlvbnMgb2YgdGhlIHBhcnRpY2xlcyBhcyB3ZWxsIGFzIHRoZWlyIGFyZWEuCgojIERhdGEgd3JhbmdsaW5nCgotIExvYWQgdGhlIHBhY2thZ2VzIGB0aWR5dmVyc2VgLCBgcmVhZHhsYCwgYHVuaXRzYCwgYGJyb29tYCwgYW5kIGBnZ2ZvcmNlYC4gV2UgYXJlIGdvaW5nIHRvIGdldHRpbmcgdXNlZCB0byB3b3JrIHdpdGggdW5pdHMgaW4gdGhpcyBleGVyY2lzZSwgd2hpY2ggaXMgYSB2ZXJ5IGdvb2QgaGFiaXQgdG8gdGFrZSBpbiBvcmRlciB0byBhdm9pZCBtYW55IHVuaXQgY29udmVyc2lvbiBwcm9ibGVtcy4gVGhlIHBhY2thZ2UgYGdnZm9yY2VgIGFsbG93cyBkb2luZyBgZ2dwbG90YCBwbG90cyB3aXRoIHRpYmJsZXMgY29udGFpbmluZyB1bml0cy4gU2V0IHRoZSBnbG9iYWwgYGdncGxvdDJgIHRoZW1lIHRvIGJsYWNrIGFuZCB3aGl0ZS4gQWxzbywgbWFrZSBpdCBzbyB0aGF0IHRoZSBgc3RyaXAuYmFja2dyb3VuZGAgKGJhY2tncm91bmQgb2YgdGhlIGZhY2V0cyB0aXRsZXMpIGlzIGJsYW5rLCBhbmQgdGhhdCB0aGUgYHN0cmlwLnRleHRgIGlzIGJvbGQuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkodW5pdHMpCmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeShicm9vbSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkrCiAgICAgICAgICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgc3RyaXAudGV4dD1lbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZT0xNCkKICAgICAgICAgICAgICAgICkpCmBgYAoKLSBGaW5kIGFsbCBNTHh4X3h4X2NjLmNzdiBmaWxlcyBpbiB0aGUgYERhdGFgIGZvbGRlciBhbmQgc3RvcmUgdGhlbSBpbiBgZmxpc3RgLgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KZmxpc3QgPC0gbGlzdC5maWxlcyhwYXRoPSJEYXRhIiwgcGF0dGVybj0ibS5jc3YiKQpgYGAKCi0gUmVhZCB0aGUgYHNhbXBsZS54bHN4YCBmaWxlIHRoYXQgY29udGFpbiBhbGwgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSB2YXJpb3VzIHNhbXBsZXMsIHN1Y2ggYXMgdGhlaXIgdGVtcGVyYXR1cmUsIHN1YnN0cmF0ZSB0eXBlLCB0aW1lIG9mIHJlYWN0aW9uLCBhbmQgc3RvcmUgdGhlIHJlc3VsdCBpbiBgc2FtcGxlc2AuIAogICAgLSBNb2RpZnkgdGhlIHRpYmJsZSBgc2FtcGxlc2Agc28gdGhhdCBpdHMgY29sdW1ucyBhcmUgbmFtZWQgInNhbXBsZSIsICJUIiwgInRpbWUiLCBhbmQgInN1YnN0cmF0ZSIKICAgIC0gVXNpbmcgYHNlcGFyYXRlKClgey5SfSwgc2VwYXJhdGUgdGhlICJzdWJzdHJhdGUiIGNvbHVtbiBpbnRvICJzdWJfdGhpY2siIGFuZCAic3ViX2FnZSIgY29udGFpbmluZyB0aGUgdGhpY2tuZXNzIGFuZCBhZ2Ugb2YgdGhlIHN1YnN0cmF0ZS4gVXNlIGBjb252ZXJ0ID0gVFJVRWAgdG8gY29udmVydCB0aGUgY2hhcmFjdGVycyB0byBpbnRlZ2VycyBpZiBhcHBsaWNhYmxlLgogICAgLSBHaXZlIHRoZSBjb2x1bW5zIHRoZWlyIHByb3BlciB1bml0IHdoZW4gYXBwbGljYWJsZSAocmVmZXIgdG8gW3RoaXMgZXhhbXBsZV0oaHR0cHM6Ly9sbWkuY25ycy5mci9yL3dvcmtpbmctd2l0aC11bml0cy5odG1sKSkKCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnNhbXBsZXMgPC0gcmVhZF9leGNlbCgiRGF0YS9zYW1wbGUueGxzeCIpICU+JSAKICAgIHJlbmFtZShzYW1wbGUgICAgPSAibmFtZSIsCiAgICAgICAgICAgVCAgICAgICAgID0gIlRlbXBlcmF0dXJlICjCsEMpIiwKICAgICAgICAgICB0aW1lICAgICAgPSAiVGltZSAobWludXRlcykiLAogICAgICAgICAgIHN1YnN0cmF0ZSA9ICJTdWJzdHJhdGUgKG5tKSIpICU+JSAKICAgIHNlcGFyYXRlKHN1YnN0cmF0ZSwgYygic3ViX3RoaWNrIiwic3ViX2FnZSIpLCBjb252ZXJ0ID0gVFJVRSkgJT4lIAogICAgbXV0YXRlKHN1Yl90aGljayA9IHNldF91bml0cyhzdWJfdGhpY2ssIm5tIiksCiAgICAgICAgICAgdGltZSAgICAgID0gc2V0X3VuaXRzKHRpbWUsIm1pbiIpLAogICAgICAgICAgIFQgICAgICAgICA9IHNldF91bml0cyhULCJkZWdDIikKICAgICAgICAgICApCmBgYAoKLSBDcmVhdGUgdGhlIGByZWFkZmlsZShmaWxlbmFtZSlgey5SfSBmdW5jdGlvbiB0aGF0LCBnaXZlbiBvbmUgb2YgdGhlIGNzdiBmaWxlcyBuYW1lcywgd2lsbDoKICAgIC0gRGV0ZXJtaW5lIHRoZSB1bml0IHVzZWQgaW4gdGhpcyBmaWxlOiBmcm9tIGBmaWxlbmFtZWAsIHN0b3JlIHRoZSB1bml0IGludG8gdGhlIHZhcmlhYmxlIGBVTklUYCAodGhhdCBzaG91bGQgYmUgdGhlIHN0cmluZyBgInVtImAgb3IgYCJubSJgKS4gWW91IGNhbiB1c2UgYHVubGlzdChzdHJzcGxpdChmaWxlbmFtZSwiXyIpKWB7LlJ9IHRvIGdldCBhIHZlY3RvciBvZiB0aGUgZWxlbWVudHMgb2YgYGZpbGVuYW1lYCBzZXBhcmF0ZWQgYnkgYSBgX2AgY2hhcmFjdGVyLgogICAgLSBSZWFkIHRoZSBjc3YgZmlsZSwgdGhlbiBzdWNjZXNzaXZlbHk6CiAgICAtIFNlbGVjdCBvbmx5IHRoZSBYLCBZIGFuZCBBcmVhIGNvbHVtbnMgKHNvbWUgZmlsZXMgaGF2ZSBtb3JlIGNvbHVtbnMpIGFuZCByZW5hbWUgdGhlbSB0byBsb3dlcmNhc2VzIG5hbWVzLgogICAgLSBBZGQgdGhlIGBmaWxlYCBjb2x1bW4gY29udGFpbmluZyB0aGUgZmlsZW5hbWUsIGFuZCB0aGVuIHNlcGFyYXRlIGl0IGludG8gMyBjb2x1bW5zIGBzYW1wbGVgLCBgbnVtYmVyYCBhbmQgYHVuaXRgCiAgICAtIFJlbW92ZSB0aGUgYHVuaXRgIGNvbHVtbgogICAgLSBBcHBseSB0aGUgY29ycmVzcG9uZGluZyB1bml0cyB0byB4LCB5IGFuZCBhcmVhLiBZb3UgY2FuIGFwcGx5IGEgdW5pdCB0byBhIHZlY3RvciBgeGAgYmFzZWQgb24gYSAqKnN0cmluZyoqIGBVTklUYCB1c2luZyBgc2V0X3VuaXRzKHgsIFVOSVQsIG1vZGUgPSAic3RhbmRhcmQiKWB7LlJ9LgogICAgLSBDcmVhdGUgYSBjb2x1bW4gYGRpYW1ldGVyYCBjb250YWluaW5nIHRoZSBkaWFtZXRlciBvZiB0aGUgcGFydGljbGVzLgotIFRlc3QgdGhpcyBmdW5jdGlvbiBvbiAyIGZpbGVzIHdpdGggMiBkaWZmZXJlbnQgdW5pdHMgdG8gY2hlY2sgdGhhdCBpdCBnaXZlcyB0aGUgZXhwZWN0ZWQgcmVzdWx0LgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KcmVhZGZpbGUgPC0gZnVuY3Rpb24oZmlsZW5hbWUpewogICAgVU5JVCA8LSB1bmxpc3Qoc3Ryc3BsaXQoZmlsZW5hbWUsIl8iKSkKICAgIFVOSVQgPC0gZ3N1YigiLmNzdiIsIiIsVU5JVFtsZW5ndGgoVU5JVCldKQogICAgcmVhZF9jc3YoZmlsZW5hbWUpICU+JSAKICAgICAgICByZW5hbWVfYWxsKHRvbG93ZXIpICU+JSAKICAgICAgICBzZWxlY3QoeCx5LGFyZWEpICU+JSAKICAgICAgICBtdXRhdGUoZmlsZT1nc3ViKCJEYXRhLyIsIiIsZmlsZW5hbWUpKSAlPiUgCiAgICAgICAgc2VwYXJhdGUoZmlsZSwgYygic2FtcGxlIiwibnVtYmVyIiwidW5pdCIpLCBzZXA9Il8iKSAlPiUgCiAgICAgICAgc2VsZWN0KC11bml0KSAlPiUgCiAgICAgICAgbXV0YXRlKHggICAgICAgID0gc2V0X3VuaXRzKHgsIFVOSVQsIG1vZGUgPSAic3RhbmRhcmQiKSwKICAgICAgICAgICAgICAgeSAgICAgICAgPSBzZXRfdW5pdHMoeSwgVU5JVCwgbW9kZSA9ICJzdGFuZGFyZCIpLAogICAgICAgICAgICAgICBhcmVhICAgICA9IHNldF91bml0cyhhcmVhLCBwYXN0ZTAoVU5JVCwiKiIsVU5JVCksIG1vZGUgPSAic3RhbmRhcmQiKSwKICAgICAgICAgICAgICAgZGlhbWV0ZXIgPSBzcXJ0KDQqYXJlYS9waSkpCn0KcmVhZGZpbGUoIkRhdGEvTUwxNl8wMl91bS5jc3YiKQpyZWFkZmlsZSgiRGF0YS9NTDE2XzA0X25tLmNzdiIpCmBgYAoKLSBVc2luZyBgcmVhZGZpbGUoKWB7LlJ9IHRoYXQgeW91IGp1c3QgZGVmaW5lZCwgcmVhZCBhbGwgY3N2IGZpbGVzIGFuZCBzdG9yZSB0aGVtIGludG8gYSB0aWR5IHRpYmJsZSBjYWxsZWQgYHBhcnRpY2xlc2AuICoqRG8gbm90IHVzZSBhIGZvciBsb29wIHRvIGRvIHNvLioqIEpvaW4gdGhpcyB0YWJsZSB3aXRoIHRoZSBgc2FtcGxlc2Agb25lLiBZb3Ugd2lsbCBzZWUgdGhhdCwgc2luY2Ugd2UgYXR0cmlidXRlZCB1bml0cyB0byBzb21lIGNvbHVtbnMsIGFsbCBkYXRhIGFyZSBhdXRvbWF0aWNhbGx5IGNvbnZlcnRlZCB0byBhIHNpbmdsZSB1bml0LiBGaWx0ZXIgdGhlIGRhdGEgZm9yIGRpYW1ldGVycyBsb3dlciB0aGFuIDQwIMK1bSBhcyBzb21lIHZlcnkgbGFyZ2UgcGFydGljbGVzIHdlcmUgZGV0ZWN0ZWQgaW4gdGhlIGltYWdlIHByb2Nlc3NpbmcgdGhhdCBhcmUgYWN0dWFsbHkgbm90IHBhcnRpY2xlcy4gVGhlIHJlc3VsdGluZyB0aWJibGUgc2hvdWxkIGxvb2sgbGlrZSB0aGlzOgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGFydGljbGVzIDwtIHRpYmJsZShmaWxlPWZsaXN0KSAlPiUgCiAgICBtdXRhdGUoZGF0YSA9IG1hcChmaWxlLCB+cmVhZGZpbGUoZmlsZS5wYXRoKCJEYXRhIiwuKSkpKSAlPiUgCiAgICB1bm5lc3QoZGF0YSkgJT4lIAogICAgaW5uZXJfam9pbihzYW1wbGVzKSAlPiUgCiAgICBmaWx0ZXIoZGlhbWV0ZXI8c2V0X3VuaXRzKDQwLCJ1bSIpKQpgYGAKCmBgYHtyfQpwYXJ0aWNsZXMKYGBgCgpgYGB7ciBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0V9CndyaXRlX3RzdihwYXJ0aWNsZXMsICJEYXRhL3BhcnRpY2xlcy5kYXQiKQpgYGAKCj4gSW4gY2FzZSB5b3UgZGlkbid0IG1hbmFnZSB0byBnZXQgdGhlcmUsIFtoZXJlXShEYXRhL3BhcnRpY2xlcy5kYXQpIGlzIHRoZSBgcGFydGljbGVzYCB0aWJibGUgKGl0IGRvZXNuJ3QgY29udGFpbiB0aGUgdW5pdHMgdGhvdWdoIGFzIHlvdSBjYW4ndCBzYXZlIGl0IGluIGEgdGV4dCBmaWxlLikKCgojIFBsb3R0aW5nIGFuZCBhbmFseXNpcwoKIyMgU2l6ZSBhbmFseXNpcwoKLSBOb3csIHBsb3QgdGhlIGhpc3RvZ3JhbSBvZiBhbGwgcGFydGljbGUgZGlhbWV0ZXJzLCB3aXRoIGEgZmlsbCBjb2xvciBkZXBlbmRpbmcgb24gdGhlIHRpbWUgKHlvdSBuZWVkIHRvIGNvbnZlcnQgdGltZSB0byBhIGZhY3RvciksIGFuZCB3aXRoIGEgZ3JpZCBzaG93aW5nIHRlbXBlcmF0dXJlIHZzLiBzdWJzdHJhdGUgYWdlIGFuZCB0aGlja25lc3MuIFB1dCB0aGUgbGVnZW5kIG9uIHRvcCBvZiB0aGUgZ3JhcGgsIGFuZCBhZGQgc29tZSB0cmFuc3BhcmVuY3kgdG8geW91ciBjb2xvcnMuCi0gSW4gZmFjdCwgSSB1c3VhbGx5IHByZWZlciB0byBwbG90IGl0IHVzaW5nIGBnZW9tX2RlbnNpdHkoKWB7LlJ9IHdoaWNoIGlzIGJhc2ljYWxseSBhbiBoaXN0b2dyYW0gY29udm9sdXRlZCB3aXRoIGEgR2F1c3NpYW4gZGlzdHJpYnV0aW9uIG9mIGJhbmR3aWR0aCBgYndgLiBUaGlzIGFsbG93cyBmb3Igc21vb3RoZXIgZ3JhcGhzLiBNYWtlIHRoaXMgcGxvdCBhbmQgcGxheSB3aXRoIHRoZSBgYndgIHBhcmFtZXRlci4KLSBDb252ZXJ0IC0tICp3aXRoIGdncGxvdCogLS0gdGhlIHVuaXQgb2YgdGhlIHBhcnRpY2xlIGRpYW1ldGVycyB0byBuYW5vbWV0ZXJzIG9yIGFueSBvdGhlciB1bml0IHlvdSB3YW50LgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGFydGljbGVzICU+JSAKICAgIGdncGxvdChhZXMoeD1kaWFtZXRlciwgZmlsbD1mYWN0b3IodGltZSkpKSsKICAgICAgICBnZW9tX2RlbnNpdHkoYWxwaGE9LjUsIGNvbG9yPU5BLCBidz00KSsKICAgICAgICBsYWJzKHggICAgPSAiYFBhcnRpY2xlIERpYW1ldGVyYCIsCiAgICAgICAgICAgICB5ICAgID0gIkRlbnNpdHkgW2FyYi4gdW5pdHNdIiwKICAgICAgICAgICAgIGZpbGwgPSAiVGltZSBbbWluXSIpKwogICAgICAgIGZhY2V0X2dyaWQocmVvcmRlcihwYXN0ZShULCJLIiksVCl+CiAgICAgICAgICAgICAgICAgICByZW9yZGVyKHBhc3RlKHN1Yl90aGljaywibm0gTmkgLSIsc3ViX2FnZSksc3ViX3RoaWNrKSwgCiAgICAgICAgICAgICAgICAgICBzY2FsZXM9ImZyZWVfeSIpKwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSsKICAgICAgICBzY2FsZV94X3VuaXQodW5pdD0ibm0iLCBsaW1pdHM9YygwLDMwMCkpCmBgYAoKLSBOb3csIHN0b3JlIGluIGBwYXJ0aWNsZXNfYXZlYCB0aGUgYXZlcmFnZSBwYXJ0aWNsZSBkaWFtZXRlciBhbmQgaXRzIHN0YW5kYXJkIGRldmlhdGlvbiBwZXIgc3Vic3RyYXRlIHRoaWNrbmVzcyBhbmQgYWdlLCB0aW1lIGFuZCB0ZW1wZXJhdHVyZSBvZiByZWFjdGlvbi4gWW91IHdpbGwgbm90ZSB0aGF0IGBtZWFuKClgey5SfSBrZWVwcyB0aGUgdW5pdCBvZiB2ZWN0b3JzIHdoaWxlIGBzZCgpYHsuUn0gbG9zZXMgaXQuIE1ha2Ugc3VyZSB0aGF0IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gY29sdW1uIGhhcyB0aGUgcHJvcGVyIHVuaXQgKHVzZSBgdW5pdHMoYSkgPC0gdW5pdHMoYilgey5SfSkuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwYXJ0aWNsZXNfYXZlIDwtIHBhcnRpY2xlcyAlPiUgCiAgICBncm91cF9ieShzdWJfdGhpY2ssIHN1Yl9hZ2UsIHRpbWUsIFQpICU+JSAKICAgIHN1bW1hcmlzZShkaWFtICAgPSBtZWFuKGRpYW1ldGVyKSwKICAgICAgICAgICAgICBzZGRpYW0gPSBzZChkaWFtZXRlcikpCnVuaXRzKHBhcnRpY2xlc19hdmUkc2RkaWFtKSA8LSB1bml0cyhwYXJ0aWNsZXNfYXZlJGRpYW0pCmBgYAoKLSBQbG90IHRoZSBhdmVyYWdlIGRpYW1ldGVyIGV2b2x1dGlvbiB3aXRoIHJlYWN0aW9uIHRpbWUsIHdpdGggYSBjb2xvciBwZXIgc3Vic3RyYXRlIHRoaWNrbmVzcywgYW5kIG9uIGEgZ3JpZCBzaG93aW5nIHN1YnN0cmF0ZSBhZ2UgdnMgdGVtcGVyYXR1cmUuCiAgICAtIERvbid0IGZvcmdldCB0byBhZGQgZXJyb3IgYmFycyBjb3JyZXNwb25kaW5nIHRvIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZGlhbWV0ZXJzIGRpc3RyaWJ1dGlvbi4KICAgIC0gQWRkIGEgbGluZSBzaG93aW5nIGEgbGluZWFyIGZpdCBmb3IgYWxsIGdyb3Vwcy4gCiAgICAtIE1ha2Ugc3VyZSBib3RoIHBsb3QgYXhlcyBnbyB0byAwLiAKICAgIC0gUHV0IHRoZSBsZWdlbmQgb24gdG9wLgogICAgLSBEZWZpbmUgbmljZSBheGlzIGxhYmVscyAod2l0aCBzZW50ZW5jZXMgaW5zdGVhZCBvZiBjb2x1bW4gbmFtZXMpLiBJbiBjYXNlIGEgY29sdW1uIGhhcyBhIHVuaXQgdGhhdCB3YXMgYXR0cmlidXRlZCwgaXQgd2lsbCByZXR1cm4gYW4gZXJyb3IgaW4gY2FzZSB0aGUgYXhpcyBsYWJlbCB5b3Ugd2FudCB0byBnaXZlIGNvbnRhaW5zIHdoaXRlIHNwYWNlcy4gWW91IHdpbGwgdGh1cyBuZWVkIHRvIHVzZSBiYWNrdGlja3MsIGxpa2Ugc286IGBgImBUd28gd29yZHNgImBgCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwYXJ0aWNsZXNfYXZlICU+JSAKICAgIGdncGxvdChhZXMoeD10aW1lLCB5PWRpYW0sIGNvbG9yPWZhY3RvcihzdWJfdGhpY2spKSkrCiAgICAgICAgZ2VvbV9wb2ludChhbHBoYT0uNSkrCiAgICAgICAgZXhwYW5kX2xpbWl0cyh4ID0gMCwgeT0wKSsKICAgICAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWRpYW0tc2RkaWFtLHltYXg9ZGlhbStzZGRpYW0pLCB3aWR0aD0uMikrCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFLCApKwogICAgICAgIGZhY2V0X2dyaWQoVH5zdWJfYWdlKSsKICAgICAgICBsYWJzKHggICAgID0gIlRpbWUiLAogICAgICAgICAgICAgeSAgICAgPSAiYEF2ZXJhZ2UgcGFydGljbGUgZGlhbWV0ZXJgIiwKICAgICAgICAgICAgIGNvbG9yID0gIlN1YnN0cmF0ZSB0aGlja25lc3MgW25tXSIpKwogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSsKICAgICAgICBzY2FsZV95X3VuaXQodW5pdD0ibm0iKQpgYGAKCi0gVXNpbmcgYGJyb29tYCBkaXNwbGF5IHRoZSBzbG9wZXMgYW5kIGludGVyY2VwdCBvZiBhbGwgbGluZWFyIGZpdHMsICoqd2l0aG91dCB1c2luZyBhIGZvciBsb29wKiouIFRoaXMgZG9lc24ndCB3b3JrIHdlbGwgd2l0aCB1bml0cywgc28gcHJpb3IgdG8gZG9pbmcgdGhlIGZpdCwgcmVtb3ZlIHRoZSB1bml0cyBvZiB5b3VyIHRpbWUgYW5kIGRpYW1ldGVyIGNvbHVtbnMgdXNpbmcgYGFzLnZlY3RvcigpYHsuUn0uCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwYXJ0aWNsZXNfYXZlICU+JSAKICAgIG11dGF0ZShkaWFtICAgPSBhcy52ZWN0b3Ioc2V0X3VuaXRzKGRpYW0sIm5tIikpLAogICAgICAgICAgIHNkZGlhbSA9IGFzLnZlY3RvcihzZXRfdW5pdHMoc2RkaWFtLCJubSIpKSwKICAgICAgICAgICB0aW1lICAgPSBhcy52ZWN0b3IodGltZSkKICAgICAgICAgICApICU+JSAKICAgIG5lc3QoZGF0YT0tYyhzdWJfdGhpY2ssc3ViX2FnZSxUKSkgJT4lIAogICAgbXV0YXRlKGZpdCAgICA9IG1hcChkYXRhLCB+bG0oZGF0YT0uLCBkaWFtfnRpbWUsIHdlaWdodHMgPSAxL3NkZGlhbSkpLAogICAgICAgICAgIHRpZGllZCA9IG1hcChmaXQsIHRpZHkpKSAlPiUgCiAgICB1bm5lc3QodGlkaWVkKSAlPiUgCiAgICBzZWxlY3Qoc3ViX3RoaWNrLHN1Yl9hZ2UsVCx0ZXJtLGVzdGltYXRlLHN0ZC5lcnJvcikgJT4lIAogICAgbXV0YXRlKHRlcm09Z3N1YigiXFwoSW50ZXJjZXB0XFwpIiwieTAiLHRlcm0pKSAlPiUgCiAgICBtdXRhdGUodGVybT1nc3ViKCJ0aW1lIiwic2xvcGUiLHRlcm0pKSAlPiUgCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdGVybSwgCiAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYyhlc3RpbWF0ZSxzdGQuZXJyb3IpKSAlPiUgCiAgICBzZXRfbmFtZXMofiBzdHJfdG9fbG93ZXIoLikgJT4lCiAgICAgICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoImVzdGltYXRlXyIsIiIpICU+JSAKICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgic3RkLmVycm9yXyIsImQiKSkgJT4lIAogICAgc2VsZWN0KHN1Yl90aGljayxzdWJfYWdlLHQseTAsZHkwLHNsb3BlLGRzbG9wZSkKYGBgCgojIyBEZW5zaXR5IGFuZCBvcmRlcmluZyBhbmFseXNpcwoKLSBOb3cgd2Ugd2FudCB0byBzZWUgdGhlIGV2b2x1dGlvbiBvZiB0aGUgZGVuc2l0eSBvZiBwYXJ0aWNsZXMgYW5kIHRoZWlyIG9yZGVyaW5nLiBPbmUgd2F5IG9mIGRvaW5nIHRoaXMgaXMgdG8gbG9vayBhdCB0aGUgKkcocikqIGRpc3RyaWJ1dGlvbiwgKmkuZS4qIHRoZSBwcm9iYWJpbGl0eSB0byBmaW5kIGEgcGFydGljbGUgaW4gKnIqIGlmIG9uZSBpcyBpbiAwLiBJIHByb3ZpZGUgaGVyZSBiZWxvdyB0aGUgZnVuY3Rpb24gYGdvZnIoeCx5LGRyLFJtYXgpYHsuUn0gdGhhdCBjb21wdXRlcyBpdCBiZXR3ZWVuIGAwYCBhbmQgYFJtYXhgIHdpdGggYSBzdGVwIGBkcmAsIHByb3ZpZGVkIHRoZSAoeCx5KSBwb3NpdGlvbnMgb2YgcGFydGljbGVzLgoKYGBge3IgaW5jbHVkZT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmdvZnIgPC0gZnVuY3Rpb24oeCwgeSwgZHI9LjIsIFJtYXg9MTApewogICAgIyBNYWtlIHN1cmUgdW5pdHMgYXJlIHVuaWZvcm0gYmVmb3JlIHVzaW5nIGRpc3QoKQogICAgdW5pdHMoeSkgPC0gdW5pdHMoeCkgCiAgICAjIGRyIGFuZCBSbWF4IGFyZSB1bml0bGVzcyBidXQgc2hvdWxkIGJlIGdpdmVuIGluIHRoZSBzYW1lIHVuaXRzIGFzIHgKICAgIGRyICAgPC0gYXMudmVjdG9yKGRyKQogICAgUm1heCA8LSBhcy52ZWN0b3IoUm1heCkKICAgICMgR2V0IGEgdmVjdG9yIG9mIGFsbCBFdWNsaWRpYW4gZGlzdGFuY2VzCiAgICBkZCAgIDwtIGFzLnZlY3RvcihkaXN0KHRpYmJsZTo6dGliYmxlKHgseSkpKQogICAgIyBNYWtlIGEgaGlzdG9ncmFtIG91dCBvZiBpdAogICAgZGQuaGlzdCA8LSBoaXN0KGRkLCAKICAgICAgICAgICAgICAgICAgICBicmVha3M9c2VxKDAsIG1heChkZCkrZHIsIGJ5PWRyKSwKICAgICAgICAgICAgICAgICAgICBwbG90PUZBTFNFKQogICAgIyBHZXQgdGhlIHIgdmFsdWVzCiAgICByIDwtIGRkLmhpc3QkbWlkcwogICAgIyBDb21wdXRlIHRoZSBub3JtYWxpemF0aW9uIGJ5IHRoZSBzdXJmYWNlIG9mIHRoZSAKICAgICMgcmluZyBvZiByYWRpdXMgciBhbmQgdGhpY2tuZXNzIGRyCiAgICBybG8gIDwtIHIgLSBkci8yCiAgICBydXAgIDwtIHIgKyBkci8yCiAgICByaW5nIDwtIHBpKihydXBeMiAtIHJsb14yKQogICAgIyBSZXR1cm4gdGhlIHRpYmJsZSBjb250YWluaW5nIHIgYW5kIEcocikgd2l0aCB0aGUgc2FtZSB1bml0IGFzIHgKICAgICMgT25seSBkYXRhIGZvciByPFJtYXggaXMgd2FudGVkLCBhbmQgd2UgcmVtb3ZlIHRoZSBmaXJzdCBlbGVtZW50IHRvbwogICAgZCA8LSB0aWJibGU6OnRpYmJsZShSICAgID0gcltyPFJtYXhdLCAKICAgICAgICAgICAgICAgICAgICAgICAgR29mUiA9IGRkLmhpc3QkY291bnRzW3I8Um1heF0vcmluZ1tyPFJtYXhdKSAlPiUgCiAgICAgICAgICAgIHJlbmFtZShyPSJSIixnb2ZyPSJHb2ZSIikKICAgIHVuaXRzKGQkcikgPC0gdW5pdHMoeCkKICAgIGRbLTEsXQp9CmBgYAoKLSBVc2luZyBwaXBlIG9wZXJhdGlvbnMsIGNvbXB1dGUgRyhyKSBmb3IgZWFjaCBpbWFnZSBhbmQgc3RvcmUgaXQgaW50byBgcGFydGljbGVzX2dvZnJgLiAKICAgIC0gV2Ugd2FudCB0byBjb21wdXRlIEcocikgdXAgdG8gNTAwIG5tIGJ5IHN0ZXAgb2YgMiBubS4gTWFrZSBzdXJlIHgsIHksIGRyIGFuZCBSbWF4IGFyZSBnaXZlbiB3aXRoIHRoZSBzYW1lIHVuaXQuCiAgICAtIFRoZW4sIGNvbXB1dGUgdGhlIGF2ZXJhZ2UgRyhyKSBmb3IgZWFjaCBzYW1wbGUuCiAgICAtIEpvaW4gYHBhcnRpY2xlc19nb2ZyYCB3aXRoIGBzYW1wbGVzYCB0byByZXRyaWV2ZSB0aGUgc2FtcGxlcyBpbmZvcm1hdGlvbgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGFydGljbGVzX2dvZnIgPC0gcGFydGljbGVzICU+JSAKICAgIG11dGF0ZSh4ID0gc2V0X3VuaXRzKHgsIm5tIiksCiAgICAgICAgICAgeSA9IHNldF91bml0cyh5LCJubSIpCiAgICAgICAgICAgKSAlPiUgCiAgICBuZXN0KGRhdGEgPSAtYyhzYW1wbGUsIG51bWJlcikpICU+JSAKICAgIG11dGF0ZShHb2ZSID0gbWFwKGRhdGEsIH5nb2ZyKC4keCwgLiR5LCBkcj0yLCBSbWF4PTUwMCkpKSAlPiUgCiAgICB1bm5lc3QoR29mUikgJT4lIAogICAgZ3JvdXBfYnkoc2FtcGxlLCByKSAlPiUgCiAgICBzdW1tYXJpemUoZ29mcj1tZWFuKGdvZnIpKSAlPiUgCiAgICBpbm5lcl9qb2luKHNhbXBsZXMpCmBgYAoKLSBGaW5kIHRoZSBiZXN0IHdheSB0byByZXByZXNlbnQgRyhyKSBmb3IgYWxsIHNhbXBsZXMsIHRoYXQgYWxsb3dzIHNlZWluZyB0aGUgZXZvbHV0aW9uIHdpdGggYWxsIHBhcmFtZXRlcnMuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwYXJ0aWNsZXNfZ29mciAlPiUgCiAgICBnZ3Bsb3QoYWVzKHg9ciwgCiAgICAgICAgICAgICAgIHk9Z29mciwgCiAgICAgICAgICAgICAgIGNvbG9yPWZhY3Rvcih0aW1lKSkpKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiLCBhbHBoYT0uNSwgc3Bhbj0uMTUsIHNlPUZBTFNFKSsKICAgICAgICBnZW9tX2xpbmUoYWxwaGE9LjIpKwogICAgICAgIGV4cGFuZF9saW1pdHMoeCA9IDAsIHk9MCkrCiAgICAgICAgZmFjZXRfZ3JpZChyZW9yZGVyKHBhc3RlKFQsIksiKSxUKX4KICAgICAgICAgICAgICAgICAgIHJlb3JkZXIocGFzdGUoc3ViX3RoaWNrLCJubSBOaSAtIixzdWJfYWdlKSxzdWJfdGhpY2spLCAKICAgICAgICAgICAgICAgICAgIHNjYWxlcz0iZnJlZV95IikrCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpKwogICAgICAgIGxhYnMoeCAgICAgPSAiciIsCiAgICAgICAgICAgICB5ICAgICA9ICJHKHIpIiwKICAgICAgICAgICAgIGNvbG9yID0gIlRpbWUgW21pbl0iKQpgYGAKCgoKCgoKCg==